package com.ijiuchuang.jc_localization.model;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PointF;

import com.ijiuchuang.jc_localization.app.Config;
import com.ijiuchuang.jc_localization.bean.cache.DeviceListCache;
import com.ijiuchuang.jc_localization.bean.cache.TagCache;
import com.ijiuchuang.jc_localization.bean.entity.Station;
import com.ijiuchuang.jc_localization.bean.event.BeaconTagData;
import com.ijiuchuang.jc_localization.bean.event.SocketGetInfoEvent;
import com.ijiuchuang.jc_localization.bean.lite.Map;
import com.ijiuchuang.jc_localization.bean.lite.Setts;
import com.ijiuchuang.jc_localization.bean.lite.Tag;
import com.ijiuchuang.jc_localization.ble.BleFilter;
import com.ijiuchuang.jc_localization.contract.LocationContract;
import com.ijiuchuang.jc_localization.rx.MapOnBytesToBeaconTagData;
import com.ijiuchuang.jc_localization.rx.MapOnNewBytesToBeaconTagData;
import com.ijiuchuang.jc_localization.rx.MapOnNewStringToBeaconTagData;
import com.ijiuchuang.jc_localization.rx.RxBus;
import com.ijiuchuang.jc_localization.tools.utils.HexUtil;
import com.ijiuchuang.jc_localization.tools.utils.Log;
import com.ijiuchuang.jc_localization.tools.utils.PositionCount;
import com.litesuits.orm.LiteOrm;
import com.litesuits.orm.db.assit.QueryBuilder;

import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import rx.Observable;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Action1;
import rx.functions.Func1;
import rx.schedulers.Schedulers;

/**
 * Created by Veev on 2016/6/2
 *
 * @link 384506557@qq.com
 */
public class LocationModel implements LocationContract.Model {

    private LiteOrm liteOrm = Config.liteOrm;

    @Override
    public void socketConnect(final DataComingListener listener) {
        Log.i("Snack is click");

        Observable
                .create(new Observable.OnSubscribe<Socket>() {
                    @Override
                    public void call(Subscriber<? super Socket> subscriber) {
                        ServerSocket server = null;
                        try {
                            server = new ServerSocket(8000);
                            server.setSoTimeout(Integer.MAX_VALUE);
                            while (true) {
                                Socket socket = server.accept();
                                subscriber.onNext(socket);
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                            subscriber.onError(e);
                        }
                    }
                })
                .subscribeOn(Schedulers.newThread())
                .observeOn(Schedulers.immediate())
                .subscribe(new Subscriber<Socket>() {
                    @Override
                    public void onCompleted() {
                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.i("onError in LocationModel socketConnect: " + e.toString());
                    }

                    @Override
                    public void onNext(Socket socket) {
                        Log.i("onNext: " + socket);
                        SocketProcess sp = new SocketProcess(socket, listener);
                        sp.start();
                    }
                });
    }

    private class SocketProcess extends Thread {
        private Socket socket;
        private DataInputStream dataInputStream = null;
        private DataComingListener listener;

        public SocketProcess(Socket socket, DataComingListener listener) {
            this.socket = socket;
            this.listener = listener;
        }
        @Override
        public void run() {
            super.run();
            // TODO: 2016/6/28 迷之缩进，用RxJava改进之
            try {
                dataInputStream = new DataInputStream(socket.getInputStream());
                while (true) {
                    if (dataInputStream.available() > 0) {
                        byte[] msg = new byte[dataInputStream.available()];
                        try {
                            dataInputStream.read(msg, 0, dataInputStream.available());
                        } catch (Exception exception) {
                            exception.printStackTrace();
                        }

                        if (msg.length >= 9) {

                            byte[] beaconMacBytes = new byte[2];
                            System.arraycopy(msg, 0, beaconMacBytes, 0, 2);
                            String uuid = HexUtil.encodeHexStr(beaconMacBytes);

                            byte[] tagMacBytes = new byte[2];
                            System.arraycopy(msg, 2, tagMacBytes, 0, 2);
                            String tagId = HexUtil.encodeHexStr(tagMacBytes);

                            Log.i("Ble: ID: " + uuid + ", rssi: " + msg[4] + ", heartRate: " + msg[6] +", Tag ID: " + tagId);
                            //EventBus.getDefault().post(new SocketGetInfoEvent(msg));
                            listener.onCame(new SocketGetInfoEvent(msg));
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    // ZigBee组网
    private List<PointF> pointFList = new ArrayList<>();
    // 滤波后rssi值
    private List<Double> preRssiList = new ArrayList<>();
    private List<Integer> limitList = new ArrayList<>();
    // WiFi组网基站
    private List<PointF> pointFList_WiFi = new ArrayList<>();
    private List<Double> preRssiList_WiFi = new ArrayList<>();
    private List<Integer> limitList_WiFi = new ArrayList<>();

    private HashMap<String, Station> zigbeeMap = new HashMap<>();
    private HashMap<Integer, String> zigbeeNumMap = new HashMap<>();

    private HashMap<String, Station> wifiMap = new HashMap<>();
    private HashMap<Integer, String> wifiNumMap = new HashMap<>();

    public LocationModel() {

        pointFList.add(0, new PointF(0f, 0f));        //light0
        pointFList.add(1, new PointF(2.0f, 7.8f));        //light1
        pointFList.add(2, new PointF(8.6f, 7.8f));        //light2
        pointFList.add(3, new PointF(5.0f, 12.25f));        //light3
        pointFList.add(4, new PointF(16.5f, 7.8f));         //light4
        pointFList.add(5, new PointF(16.5f, 7.8f));         //light5

        for (int i = 0; i <= 5; i++) {
            preRssiList.add((double) 0);
        }

        limitList.add(0, -85);                 //light0
        limitList.add(1, -85);                 //light1
        limitList.add(2, -85);                 //light2
        limitList.add(3, -85);                 //light3
        limitList.add(4, -85);                 //light4
        limitList.add(5, -85);                 //light5

        zigbeeMap.put("0101", new Station(1, new PointF(2.0f, 7.8f), -80, "0101"));
        zigbeeMap.put("0202", new Station(2, new PointF(8.6f, 7.8f), -80, "0202"));
        zigbeeMap.put("0303", new Station(3, new PointF(5.0f, 12.25f), -80, "0303"));
        zigbeeMap.put("0404", new Station(4, new PointF(16.5f, 7.8f), -80, "0404"));
        zigbeeMap.put("123456789004", new Station(5, new PointF(16.5f, 7.8f), -100, "123456789004"));
        zigbeeNumMap.put(1, "0101");
        zigbeeNumMap.put(2, "0202");
        zigbeeNumMap.put(3, "0303");
        zigbeeNumMap.put(4, "0404");
        zigbeeNumMap.put(5, "123456789004");

        wifiMap.put("111111111111", new Station(1, new PointF(17.0f, 7.70f), -95, "111111111111"));
        wifiMap.put("222222222222", new Station(2, new PointF(13.5f, 7.70f), -95, "222222222222"));
        wifiMap.put("333333333333", new Station(3, new PointF(9.5f, 7.70f), -87,  "333333333333"));
        wifiMap.put("444444444444", new Station(4, new PointF(5.5f, 7.70f), -87,  "444444444444"));
        wifiMap.put("555555555555", new Station(5, new PointF(1.5f, 7.70f), -95,  "555555555555"));
        wifiMap.put("666666666666", new Station(6, new PointF(2.0f, 9.90f), -100, "666666666666"));
        wifiMap.put("777777777777", new Station(7, new PointF(8.5f, 8.90f), -95,  "777777777777"));
        wifiMap.put("888888888888", new Station(8, new PointF(8.5f, 12.85f), -95,  "888888888888"));
        wifiMap.put("999999999999", new Station(9, new PointF(8.5f, 11.15f), -88,  "999999999999"));
        wifiMap.put("101010101010", new Station(10, new PointF(2.0f, 12.85f), -90,"101010101010"));

        wifiNumMap.put(1, "111111111111");
        wifiNumMap.put(2, "222222222222");
        wifiNumMap.put(3, "333333333333");
        wifiNumMap.put(4, "444444444444");
        wifiNumMap.put(5, "555555555555");
        wifiNumMap.put(6, "666666666666");
        wifiNumMap.put(7, "777777777777");
        wifiNumMap.put(8, "888888888888");
        wifiNumMap.put(9, "999999999999");
        wifiNumMap.put(10, "101010101010");

        pointFList_WiFi.add(0, new PointF(0f, 0f));
        pointFList_WiFi.add(1, new PointF(17.0f, 7.70f));
        pointFList_WiFi.add(2, new PointF(13.5f, 7.70f));
        pointFList_WiFi.add(3, new PointF(9.5f, 7.70f));
        pointFList_WiFi.add(4, new PointF(5.5f, 7.70f));
        pointFList_WiFi.add(5, new PointF(1.5f, 7.70f));
        pointFList_WiFi.add(6, new PointF(2.0f, 9.90f));
        pointFList_WiFi.add(7, new PointF(8.5f, 8.90f));
        pointFList_WiFi.add(8, new PointF(8.5f, 12.85f));
        pointFList_WiFi.add(9, new PointF(8.5f, 11.15f));
        pointFList_WiFi.add(10, new PointF(2.0f, 12.85f));

        for (int i = 0; i <= 10; i++) {
            preRssiList_WiFi.add((double) -100);
        }

        limitList.add(0, -90);
        limitList.add(1, -90);
        limitList.add(2, -90);
        limitList.add(3, -90);
        limitList.add(4, -90);
        limitList.add(5, -90);
        limitList.add(6, -100);
        limitList.add(7, -90);
        limitList.add(8, -90);
        limitList.add(9, -90);
        limitList.add(10, -90);

    }

    @Override
    public List<PointF> getPoint() {
        return pointFList;
        //return pointFList_WiFi;
    }

    @Override
    public void loadMap(final Context context, Action1<Bitmap> action1) {
        QueryBuilder<Setts> equals = new QueryBuilder<>(Setts.class).whereEquals(Setts.COL_KEY, Setts.SETTINGS_MAP_SCENE);
        ArrayList<Setts> list = liteOrm.query(equals);
        final long count = liteOrm.queryCount(Map.class);
        long position = 1;

        int flag = -1;

        if (list.isEmpty()) {
            // 设置为空 说明没有设置默认地图
            if (count == 0) {
                // count 为 0， 说明没有地图， 取第一张图片
                flag = 0;
            } else {
                // 没有设置默认地图，但是有地图数据，则取第一个地图数据
                flag = 1;
            }
        } else {
            //不为空
            position = list.get(0).getValueLong();
            flag = 2;
        }

        final int finalFlag = flag;
        final long finalPosition = position;
        Observable
                .create(new Observable.OnSubscribe<Bitmap>() {
                    @Override
                    public void call(Subscriber<? super Bitmap> subscriber) {
                        try {
                            if (finalFlag == 2) {
                                Map map = liteOrm.queryById(finalPosition, Map.class);
                                InputStream is = context.getAssets().open("image/map/" + map.getMapUrl());
                                Bitmap bitmap= BitmapFactory.decodeStream(is);
                                subscriber.onNext(bitmap);
                            } else if (finalFlag == 1){
                                Map map = liteOrm.queryById(1, Map.class);
                                InputStream is = context.getAssets().open("image/map/" + map.getMapUrl());
                                Bitmap bitmap= BitmapFactory.decodeStream(is);
                                subscriber.onNext(bitmap);
                            } else {
                                String files[] = context.getAssets().list("image/map");
                                InputStream is = context.getAssets().open("image/map/" + files[0]);
                                Bitmap bitmap= BitmapFactory.decodeStream(is);
                                subscriber.onNext(bitmap);
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                            subscriber.onError(e);
                        }
                    }
                })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(action1, new Action1<Throwable>() {
                    @Override
                    public void call(Throwable throwable) {
                        Log.i("onError in LocationModel loadMap: " + throwable.toString());
                    }
                });
    }

    // 节点编号
    static int nodeNumber = 1;
    static PointF posXY = new PointF();
    private PositionCount positionCount = new PositionCount(-111, -111, -111, -111);

    private BeaconTagData data0 = new BeaconTagData();

    /**
     * TODO 待优化 2016年6月15日16:26:14
     * @param socketGetInfoEvent 数据
     * @param listener 监听
     */
    @Override
    public void locationProcess(final SocketGetInfoEvent socketGetInfoEvent, final LocationChangeListener listener) {
        // 将socket收到的bytes转为Tag
        Observable.just(socketGetInfoEvent.getData())
                .filter(new Func1<byte[], Boolean>() {
                    @Override
                    public Boolean call(byte[] bytes) {
                        return bytes.length % 9 == 0;
                    }
                })
                .map(new Func1<byte[], String>() {
                    @Override
                    public String call(byte[] bytes) {
                        return HexUtil.encodeHexStr(bytes);
                    }
                })
                //.map(MapOnNewBytesToBeaconTagData.getInstance())
                .flatMap(new Func1<String, Observable<String>>() {
                    @Override
                    public Observable<String> call(String msgStr) {
                        // 去掉字符串中的 0d0a
                        String regex = "0d0a";
                        String processStr = msgStr.replaceAll(regex, "");
                        int len = processStr.length();

                        // 长度为34， 等长分割
                        String regexSplit = "(?<=\\G.{18})";
                        String strArr[]  = processStr.split(regexSplit);
                        return Observable.from(strArr);
                    }
                })
                .map(MapOnNewStringToBeaconTagData.getInstance())
                .filter(new Func1<BeaconTagData, Boolean>() {
                    @Override
                    public Boolean call(BeaconTagData beaconTagData) {
                        return beaconTagData.rssi < 0;
                    }
                })
                .subscribe(new Action1<BeaconTagData>() {
                    @Override
                    public void call(BeaconTagData beaconTagData) {
                        data0 = beaconTagData;
                        concat(data0, listener);
                    }
                });
    }

    private void concat(final BeaconTagData data, final LocationChangeListener listener) {
        // 从缓存拿要显示的点
        Observable<TagCache> cache = Observable.create(new Observable.OnSubscribe<TagCache>() {
            @Override
            public void call(Subscriber<? super TagCache> subscriber) {
                if (DeviceListCache.getInstance().isCached(data.tagUuid)) {
                    subscriber.onNext(DeviceListCache.getInstance().get(data.tagUuid));
                } else {
                    subscriber.onCompleted();
                }
            }
        });

        // 从数据库拿要显示的点
        Observable<TagCache> database = Observable.create(new Observable.OnSubscribe<TagCache>() {
            @Override
            public void call(Subscriber<? super TagCache> subscriber) {
                QueryBuilder<Tag> equals = new QueryBuilder<>(Tag.class)
                        .whereEquals(Tag.COL_MAC, data.tagUuid);
                if (liteOrm.query(equals) != null) {
                    ArrayList<Tag> list = liteOrm.query(equals);
                    if (list.size() != 0) {
                        TagCache tagCache = new TagCache(list.get(0).getMac(), list.get(0).getName(), list.get(0).isShow());
                        DeviceListCache.getInstance().put(data.tagUuid, tagCache);
                        subscriber.onNext(tagCache);
                    } else {
                        subscriber.onCompleted();
                    }
                } else {
                    subscriber.onCompleted();
                }
            }
        });

        // 都没有，新建一个
        Observable<TagCache> create = Observable.create(new Observable.OnSubscribe<TagCache>() {
            @Override
            public void call(Subscriber<? super TagCache> subscriber) {
                TagCache tagCache = new TagCache(data.tagUuid, "", false);
                Tag tag = new Tag(data.tagUuid.trim(), "");
                liteOrm.save(tag);
                Log.i("发现新设备： " + data.tagUuid);
                subscriber.onNext(tagCache);
            }
        });

        // 三级缓存
        Observable.concat(cache, database, create)
                .first()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .filter(new Func1<TagCache, Boolean>() {
                    @Override
                    public Boolean call(TagCache tagCache) {
                        return tagCache.isShow;
                    }
                })
                .subscribe(new Subscriber<TagCache>() {
                    @Override
                    public void onCompleted() {
                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.i("error: " + e.toString());
                    }

                    @Override
                    public void onNext(TagCache tagCache) {

                        if (true) {
                            if (zigbeeMap.containsKey(data.beaconUuid)) {
                                Station station = zigbeeMap.get(data.beaconUuid);
                                station.preRssi = BleFilter.KalmanFiltering(station.preRssi, data.rssi);
                                zigbeeMap.put(data.beaconUuid, station);
                                // 无需比较 干掉
                                // Station station1 = zigbeeMap.get(zigbeeNumMap.get(positionCount.getPosition(station.id, station.preRssi)));
                                String logStr = "Node: " + station.id +", uuid: " + data.tagUuid +
                                        ", preRssi: " + station.preRssi + ", rssi: " + data.rssi + ", heartR: " + data.heartRate;
                                Log.i(logStr);
                                RxBus.getInstance().post(logStr);
                                // writeFileSdcard("Node" + station.id, data.rssi+"");
                                if (station.preRssi >= station.limit) {
                                    Log.i(String.format("我在%d号点，心率:%d", station.id, data.heartRate));
                                    listener.onChanged(tagCache.mac, tagCache.name, station.pointF);
                                }
                            } else {
                                return;
                            }
                        } else {
                            if (wifiMap.containsKey(data.beaconUuid)) {
                                Station station = wifiMap.get(data.beaconUuid);
                                station.preRssi = BleFilter.CommonFiltering(station.preRssi, data.rssi);
                                //station.preRssi = BleFilter.KalmanFiltering(station.preRssi, data.rssi);
                                Log.i("Node: " + station.id +", uuid: " + data.tagUuid + ", preRssi: " + station.preRssi + ", rssi: " + data.rssi);
                                //writeFileSdcard("Node" + station.id + ".txt", "" + data.rssi);
                                if (station.preRssi > station.limit) {
                                    listener.onChanged(tagCache.mac, tagCache.name, station.pointF);
                                }
                            } else {
                                return;
                            }
                        }
                    }
                });
    }

    /**
     * 追加写入文本
     * @param fileName 文件
     * @param content 数据
     */
    // 写在/mnt/sdcard/目录下面的文件
    public void writeFileSdcard(String fileName, String content) {
        try {
            File file = new File(Config.iJiuChuang_Folder + "/" + fileName + ".txt");
            BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
                    new FileOutputStream(file, true)));
            out.write(content + "\r\n");
            out.close();
        } catch (java.io.IOException e) {
            e.printStackTrace();
            Log.i("Error: " + e.toString());
        }
    }
}
